/*
	Copyright (c) 2010, Mathias Myrland, Myrland Systemutvikling (mathias@myrsys.no)
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:
	
	    * Redistributions of source code must retain the above copyright
	      notice, this list of conditions and the following disclaimer.
	      
	    * Redistributions in binary form must reproduce the above copyright
	      notice, this list of conditions and the following disclaimer in the
	      documentation and/or other materials provided with the distribution.
	      
	    * Neither the name of Myrland Systemutvikling nor the
	      names of its contributors may be used to endorse or promote products
	      derived from this software without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
	DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
	DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CGF_COMPONENT_HPP
#define CGF_COMPONENT_HPP

#include <cgf/enginetypes.hpp>
#include <cgf/signal.hpp>
#include <cgf/object.hpp>

#include <string>

#include <boost/unordered_set.hpp>
namespace CGF
{

	
	/**	A Shared pointer type to the Component class	*/
	typedef boost::shared_ptr<Component> ptr_Component;
	
	/**	A Weak pointer type to the Component class	*/
	typedef boost::weak_ptr<Component> ptr_w_Component;
	
	class ComponentTemplate;
	
	/**
	*	The component class is the base of the component based object system.
	*	Events are named slots that can be listened to, and will be signaled when their condition is met
	*	*/
	class Component : public SignalProducer
	{
		
		/**
		*	Class for storing the attributes of a component	*/
		class Attribute : public Object<Attribute>
		{
			public:
		};
		
		/**
		*	Templated attribute function. 
		*	@tparam attType The datatype of this attribute	*/	
		template<class attType>
		class Attribute_T : public Attribute
		{
			attType m_value;
			public:
				attType value() 				{ return m_value; }
				void setValue ( attType v )			{ m_value = v; }
				Attribute_T ( attType v ) : m_value ( v )	{}
		};
		
		/**	The name is the unique identifier for this component. It is the ID of the component in any form or fashion.	*/
		std::string m_name;
		
		/**	A set containing all the classes this component belongs to	*/
		boost::unordered_set<std::string> m_classes;
		
		/**	A map with all the attribute pointers associated with this property	*/
		boost::unordered_map<std::string, Attribute::ptr > m_attributes;
		
		/**	The owner of this particular component. We have to use a weak pointer type to avoid circular dependencies.	*/
		ptr_w_Entity m_owner;
		
		/**	The importance rating of this component.	*/
		size_t m_rating;
		
		public:
			
		
			/**
			*	Looks up a class name, and returns true if it is present in the classes set.
			*	@param	className	The class name to look up
			*	@return			The boolean value of the count of elements equal to the className parameter	*/
			bool hasClass ( std::string );
		
			/**
			*	Set the name of the component
			*	@param	n	The name to set	*/
			void setName ( std::string );
			
			/**
			*	Getter for the name
			*	@return			The name of the component	*/
			inline const std::string& name() { return m_name; }
			/**
			*	Add a class to this component
			*	@param	class	The class name to add	*/
			void addClass( std::string );
			
			/**
			*	@return			The importance rating of this component	*/
			size_t importance() { return m_rating; }
			
			/**
			*	Since the base component itself is an abstract class, its Update function does nothing in itself.  Every derived component type must
			*	specify its own logic behaviour.	*/
			virtual void update() {}
			
			/**
			*	Add an attribute to the component
			*
			*	@tparam 	valuetype 	The valuetype of the attribute we are adding
			*	@param	name		The key name of the attribute
			*	@param	value		The value to add				*/
			template <class valuetype>
			void setAttribute ( std::string attName, valuetype value )
			{
				Attribute::ptr created ( new Attribute_T<valuetype> ( value ) );
				m_attributes[attName] = created;
			}
			
			/**
			*	Add an attribute to the component
			*
			*	@tparam 	valuetype	The valuetype of the attribute we are adding
			*	@param	name		The key name of the attribute	*/
			template <class valuetype>
			valuetype getAttribute ( std::string attName )
			{
				return ((Attribute_T<valuetype>*)m_attributes[attName].get())->value();
			}
			
			/**
			*	Construct the component with the provided entity as the owner
			*	@param owner	The entity which owns this component	*/
			Component( ptr_Entity );
			
			/**
			*	Copy constructor which constructs the component based on a template, and the provided entity as the owner
			*
			*	@param template	The template to copy
			*	@param owner	The entity which owns this component	*/
			Component( boost::shared_ptr<ComponentTemplate>, ptr_Entity );
				
			/**
			*	Base constructor	*/
			Component();
			
			virtual ~Component();
	};
	
	/**
	*	Comparison function used to sort containers with the type ptr_Component
	*	@param	lhs		The first component
	*	@param	rhs		The component to compare with	*/
	bool operator< ( ptr_Component, ptr_Component );
	
	/**
	*	Unary predicate used to remove components of a specific class from a list
	*	@param cname	The name of the class to remove	*/
	bool removeClassPredicate  ( std::string );
}// namsepace CGF

#endif
